
package com.tools.cleanmaster.model.db;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.text.TextUtils;

import com.tools.cleanmaster.CleanApplication;
import com.tools.cleanmaster.controller.TraversalCacheAndResidualScanner;
import com.tools.cleanmaster.model.item.FilePathBean;
import com.tools.cleanmaster.model.item.PathUpdateInfo;
import com.tools.cleanmaster.model.language.LanguageCompat;
import com.tools.cleanmaster.store.TrashCloudSharedPrefManager;
import com.tools.cleanmaster.utils.FeatureConfig;
import com.tools.cleanmaster.utils.FileUtils;
import com.tools.cleanmaster.utils.LogHelper;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class TrashDB extends SQLiteOpenHelper {
    private static final int PKG_LEN = 20;
    private static final String TAG = "CacheTrashDB";
    //本地库路径更新初始时间
    private static final long NATIVE_TIME = 1446307200001l;// 当前下发数据的最后更新时间,只有在DB取出time为0时使用

    private static final int UPDATE = 0;

    public static final int CLOUD = 1;

    //使用默认扫描器
    public static final int DEFAULT = -1;

    //查询深度
    public static final int DEEP = 0;

    //查询快速
    public static final int QUICK = 1;

    //查询所有
    public static final int ALL = 2;

    //云端下发数据更新
    public static final int CLOUD_UPDATE = 10;

    //本地数据更新
    public static final int NATIVE_UPDATE = 11;

    //未知路径更新
    public static final int UNKNOWN_UPDATE = 12;

    //云端更新上传路径数量
    public static final String ClOUD_UPDATE_NUM = "10";

    public static final String KEY = "dianxinos";
    //卸载残留弹窗
    static final String REDULE_SQL_BY_PKG = "select j.a a,j.c c, j.[d] d,f.[b] e,a.b f,j.h h, f.[a] i from j " +
            "left join d on d.a = j.[b] left join f on f.[a]=j.[e] " +
            "left join a on a.[a]=j.[f] where a.c = 1 and j.c like (\"%\" || ? || \"%\");";
    //快速-残留
    static final String REDULE_SQL_QUICK = "select j.a a, j.c c, j.[d] d,f.[b] e,a.b f,j.h h,f.[a] i from j " +
            "left join d on d.a = j.[b]  left join f on f.[a]=j.[e] " +
            "left join a on a.[a]=j.[f] left join o on f.[c]=o.[a] where j.h = 1 and a.c = 1 and o.b in ";
    //快速-缓存
    static final String CACHE_SQL_QUICK =
            "select m.a a,h.[b] b,f.[b] c,a.b e,d.[c] d ,e.c h,m.[g] g,m.[h] f,f.[a] i,m.[j] j,e.b k " +
                    " from m left join h on h.a = m.[b] left join f on f.a = m.[c] left join a on m.e = a.a " +
                    "left join d on m.f = d.a left join e on m.i = e.a left join o on f.[c]=o.[a] where m.g = 1 and e"
                    + ".b in (?,1) and a.c = 1 ";

    //专清-缓存(快速+深度)
    static final String CACHE_APPCLEAN_SQL =
            "select m.a a,h.[b] b,f.[b] c,a.b e,d.[c] d ,e.c h,m.[g]  g,m.[h] f,f.[a] i,m.[j] j " +
                    " from m left join h on h.a = m.[b] left join f on f.a = m.[c] left join a on m.e = a.a " +
                    "left join d on m.f = d.a left join e on m.i = e.a left join o on f.[c]=o.[a] where e.b = 1 and a"
                    + ".c = 1 ";

    //正则-快速
    static final String REGULARS_SQL_QUICK =
            "select i.[a] a,e.[c] b,h.[b] c,i.[d] d,i.[e] e,a.[b] f,i.[g] g,i.[h] h,i.[i] i,i.[j] j,e.b k " +
                    "from i left join h on h.a = i.c left join a on i.f = a.a left join e on i.b = e.[a] " +
                    "where i.g = 1 and e.b in (?,1) and a.c = 1 and h.[b] in ";
    //深度-残留
    static final String REDULE_SQL_DEEP = "select j.a a, j.c c, j.[d] d,f.[b] e,a.b f,j.h h,f.[a] i from j " +
            "left join d on d.a = j.[b]  left join f on f.[a]=j.[e] " +
            "left join a on a.[a]=j.[f] left join o on f.[c]=o.[a] where j.h = 0 and a.c = 1 and o.b in ";
    //深度-缓存
    static final String CACHE_SQL_DEEP =
            "select m.a a,h.[b] b,f.[b] c,a.b e,d.[c] d ,e.c h,m.[g]  g,m.[h] f,f.[a] i,m.[j] j,e.b k " +
                    " from m left join h on h.a = m.[b] left join f on f.a = m.[c] left join a on m.e = a.a " +
                    "left join d on m.f = d.a left join e on m.i = e.a left join o on f.[c]=o.[a] where m.g = 0 and e"
                    + ".b in (?,1) and a.c = 1 ";
    //正则-深度
    static final String REGULARS_SQL_DEEP =
            "select i.[a] a,e.[c] b,h.[b] c,i.[d] d,i.[e] e,a.[b] f,i.[g] g,i.[h] h,i.[i] i,i.[j] j,e.b k " +
                    "from i left join h on h.a = i.c left join a on i.f = a.a left join e on i.b = e.[a] " +
                    "where i.g = 0 and e.b in (?,1) and a.c = 1 and h.[b] in ";
    //正则-all
    static final String REGULARS =
            "select i.[a] a,e.[c] b,h.[b] c,i.[d] d,i.[e] e,a.[b] f,i.[g] g,i.[h] h,i.[i] i,i.[j] j,e.b k  " +
                    "from i left join h on h.a = i.c left join a on i.f = a.a left join e on i.b = e.[a] " +
                    "where e.b in (?,1) and a.c = 1 and h.[b] in ";
    //缓存-all
    static final String CACHE =
            "select m.a a,h.[b] b,f.[b] c,a.b e,d.[c] d ,e.c h,m.[g] g,m.[h] f,f.[a] i,m.[j] j,e.b k " +
                    " from m left join h on h.a = m.[b] left join f on f.a = m.[c] left join a on m.e = a.a " +
                    "left join d on m.f = d.a left join e on m.i = e.a left join o on f.[c]=o.[a] where e.b in (?,1) "
                    + "and a.c = 1 ";
    //残留-all
    static final String REDULE = "select j.a a, j.c c, j.[d] d,f.[b] e,a.b f,j.h h,f.[a] i from j " +
            "left join d on d.a = j.[b]  left join f on f.[a]=j.[e] " +
            "left join a on a.[a]=j.[f] left join o on f.[c]=o.[a] where a.c = 1 and o.b in ";
    static final String[] TABLENAMES = {
            "a", // Table: AlterInfo 0
            "b", // Table: AppName 1
            "c", // Table: CleanLog　delete 2
            "d", // Table: ContentType 3
            "e", // Table: Description 4
            "f", // Table: FilePath 5
            "g", // Table: Lang 6
            "h", // Table: Pkg 7
            "i", // Table: Regular 8
            "j", // Table: ResidualTrash 9
            "k", // Table: WhiteList 10
            "l", // Table: adsClean delete 11
            "m", // Table: cache 12
            "n",// Table: 递归缓存数据，方便下次递归跳过，定期会失效 13
            "o" //Table: 存放一级路径 14
    };

    private static final int TABLE_WHITE_LIST = 10;
    private static final int DEFAULT_LANGUAGE = 1;//默认英语
    private static final String[] WHITELIST =
            new String[]{"/mobcent", "/android/obb", "/data", "/android", "/tencent", "/android/data", "/data/data", "/dcim", "/pictures"};
    public static final String DATABASE_NAME = "TRASH_DB";
    public static final int DATABASE_VERSION = 10;

    private static TrashDB sInstance;

    public static TrashDB getInstance() {
        if (sInstance == null) {
            synchronized (TrashDB.class) {
                if (sInstance == null) {
                    TrashDBPuller.copyAssetDb();
                    //数据库有变动,设置未云扫过
                    TrashCloudSharedPrefManager.setCloudyScanReport(false);
                    sInstance = new TrashDB(CleanApplication.getInstance());
                }
            }
        }

        return sInstance;
    }

    //保证写入db同步
    private final static Object mWriteLock = new Object();

    private SQLiteDatabase db = null;

    private TrashDB(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        // 使用了预置数据，不需要创建表
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if (FeatureConfig.DEBUG_LOG) {
            LogHelper.i(TAG, "AppDatabase onUpgrade from " + oldVersion + " to "
                    + newVersion + ".");
        }
    }

    //获取db
    private void initDatabase() {
        if (db == null || !db.isOpen()) {
            db = getWritableDatabase();
        }
    }

    /**
     * @param pkgNames
     * @return if pkgNames size is zero will return null;
     */
    public static String getPkgNamesSQL(List<String> pkgNames) {
        int len = pkgNames.size();
        if (len == 0) {
            return null;
        }
        StringBuilder sb = new StringBuilder(len * PKG_LEN);
        sb.append(" (");
        for (int i = 0; i < len; i++) {
            if (TextUtils.isEmpty(pkgNames.get(i))) {
                continue;
            } else {
                sb.append("\"").append(pkgNames.get(i))//
                        .append("\"");
                if (i + 1 < len) {
                    sb.append(",");
                }
            }
        }
        sb.append(") ");
        return sb.toString();
    }

    /**
     * get all Trash
     *
     * @param rootPath  sdCard root path list
     * @param pkgNames  all installed App package name list
     * @param queryType
     * @return
     */
    public AllTrashBean getAllTrash(List<String> rootPath, final List<String> pkgNames,
                                    int queryType) {
        if (pkgNames == null || rootPath == null) {
            return null;
        }
        String pkgNamesSQL = getPkgNamesSQL(pkgNames);
        if (pkgNamesSQL == null) {
            return null;
        }
        String path = getPkgNamesSQL(rootPath);
        DBBeanFilter<ResidualTrashBean> df = new TrashDBBeanFilter(pkgNames);
        if (TextUtils.isEmpty(path)) {
            return null;
        }
        List<CacheTrashBean> findCacheTrashByFilePath = findCacheTrashByFilePath(path, pkgNamesSQL,
                queryType, false);
        List<ResidualTrashBean> findResidualTrashByFilePath = findResidualTrashByFilePath(path, df,
                queryType, false);
        AllTrashBean allTrashBean = null;
        if (findCacheTrashByFilePath != null && !findCacheTrashByFilePath.isEmpty()) {
            allTrashBean = new AllTrashBean();
            for (CacheTrashBean cb : findCacheTrashByFilePath) {
                cb.filePath = DataBaseDESUtil.decryptStr(cb.filePath, KEY);
            }
            allTrashBean.cacheTrashBean = findCacheTrashByFilePath;
        }
        if (findResidualTrashByFilePath != null && !findResidualTrashByFilePath.isEmpty()) {
            if (allTrashBean == null) {
                allTrashBean = new AllTrashBean();
            }
            for (ResidualTrashBean rb : findResidualTrashByFilePath) {
                rb.filePath = DataBaseDESUtil.decryptStr(rb.filePath, KEY);
            }
            allTrashBean.residualTrashBean = findResidualTrashByFilePath;
        }
        return allTrashBean;
    }

    /**
     * 后台递归扫描使用，支持快速，深度，快速/深度通知扫描
     */
    public Map<String, AllTrashBean> getTraversalAllTrash(List<String> rootPath, int queryType,
                                                          Map<String, String> rootPathMap) {
        if (rootPath == null) {
            return null;
        }

        String path = getPkgNamesSQL(rootPath);
        if (TextUtils.isEmpty(path)) {
            return null;
        }

        List<CacheTrashBean> findCacheTrashByFilePath = findCacheTrashByFilePath(path, null,
                queryType, true);
        List<ResidualTrashBean> findResidualTrashByFilePath = findResidualTrashByFilePath(path, null,
                queryType, true);

        Map<String, AllTrashBean> map = new HashMap<String, AllTrashBean>();
        //对缓存垃圾进行拆分处理
        if (findCacheTrashByFilePath != null) {
            for (int i = 0; i < findCacheTrashByFilePath.size(); i++) {
                CacheTrashBean cacheTrashBean = findCacheTrashByFilePath.get(i);
                cacheTrashBean.filePath = DataBaseDESUtil.decryptStr(cacheTrashBean.filePath, KEY);
                if (!TextUtils.isEmpty(cacheTrashBean.filePath)) {
                    if (cacheTrashBean.filePath.startsWith("/")) {
                        cacheTrashBean.rootPath = cacheTrashBean.filePath.split("/")[1];
                    } else {
                        cacheTrashBean.rootPath = cacheTrashBean.filePath.split("/")[0];
                    }
                    cacheTrashBean.rootPath = FileUtils.addFrontSlash(cacheTrashBean.rootPath);
                    AllTrashBean trashBean = map.get(cacheTrashBean.rootPath);
                    if (trashBean == null) {
                        trashBean =
                                new AllTrashBean(new ArrayList<ResidualTrashBean>(), new ArrayList<CacheTrashBean>());
                        map.put(cacheTrashBean.rootPath, trashBean);
                        if (FeatureConfig.DEBUG_LOG) {
                            LogHelper.i(TAG, i + "  cache add / map add " + cacheTrashBean);
                        }
                    }
                    trashBean.cacheTrashBean.add(cacheTrashBean);
                }
            }

        }
        //对残留进行拆分处理
        if (findResidualTrashByFilePath != null) {
            for (int i = 0; i < findResidualTrashByFilePath.size(); i++) {
                ResidualTrashBean residualTrashBean = findResidualTrashByFilePath.get(i);
                residualTrashBean.filePath = DataBaseDESUtil.decryptStr(residualTrashBean.filePath, KEY);
                if (!TextUtils.isEmpty(residualTrashBean.filePath)) {
                    if (residualTrashBean.filePath.startsWith("/")) {
                        residualTrashBean.rootPath = residualTrashBean.filePath.split("/")[1];
                    } else {
                        residualTrashBean.rootPath = residualTrashBean.filePath.split("/")[0];
                    }
                    residualTrashBean.rootPath = FileUtils.addFrontSlash(residualTrashBean.rootPath);
                    AllTrashBean trashBean = map.get(residualTrashBean.rootPath);
                    if (trashBean == null) {
                        trashBean =
                                new AllTrashBean(new ArrayList<ResidualTrashBean>(), new ArrayList<CacheTrashBean>());
                        map.put(residualTrashBean.rootPath, trashBean);
                        if (FeatureConfig.DEBUG_LOG) {
                            LogHelper.i(TAG, i + "  residula add / map add " + residualTrashBean);
                        }
                    }
                    trashBean.residualTrashBean.add(residualTrashBean);
                }
            }
        }
        for (String s : rootPath) { // 添加未在数据库中的一级路径，递归路径需要
            String p = FileUtils.addFrontSlash(rootPathMap.get(s));
            if (p != null) {
                AllTrashBean all = map.get(p);
                if (all == null) {
                    map.put(p, null);
                }
            }
        }
        return map;
    }

    /**
     * find Residue of Trash
     *
     * @param filePath
     * @param df
     * @param queryType
     * @param isTraversal
     * @return all records if it file path name like file path
     */
    private List<ResidualTrashBean> findResidualTrashByFilePath(String filePath,
                                                                DBBeanFilter<ResidualTrashBean> df, int queryType,
                                                                boolean isTraversal) {
        Cursor cursor = null;
        try {
            String sql = "";
            if (queryType == DEEP) {
                sql = REDULE_SQL_DEEP;
            } else if (queryType == QUICK) {
                sql = REDULE_SQL_QUICK;
            } else if (queryType == ALL) {
                sql = REDULE;
            }
            initDatabase();
            cursor = db.rawQuery(sql + filePath, null);
            List<ResidualTrashBean> datas;
            if (df == null) {
                return ResidualTrashBean.fromCursor(cursor, null);
            }
            if (!isTraversal) {
                datas = ResidualTrashBean.fromCursor(cursor, df);
            } else {
                datas = ResidualTrashBean.fromCursor(cursor, null);
            }
            if (datas != null && !datas.isEmpty()) {// check datas
                final int length = datas.size();
                for (int i = length - 1; i >= 0; i--) {
                    if (TextUtils.isEmpty(datas.get(i).filePath)) {
                        datas.remove(i);
                    }
                }
            }
            return datas;
        } catch (Exception e) {
            if (FeatureConfig.DEBUG_LOG) {
                throw new RuntimeException(e); // 推荐在 DEBUG 模式下，重新抛出这个异常，以便尽早的发现问题
            }
            return null;
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    /**
     * find cache Trash
     *
     * @param filePath
     * @param pkgNames    like ('a','b','c')
     * @param queryType
     * @param isTraversal
     * @return
     */
    private List<CacheTrashBean> findCacheTrashByFilePath(String filePath, String pkgNames, int queryType,
                                                          boolean isTraversal) {
        Cursor cursor = null;
        final StringBuilder sql = new StringBuilder();
        if (queryType == DEEP) {
            sql.append(CACHE_SQL_DEEP);
        } else if (queryType == QUICK) {
            sql.append(CACHE_SQL_QUICK);
        } else if (queryType == ALL) {
            sql.append(CACHE);
        }
        if (!isTraversal) {
            sql.append(" and h.b in ");
            sql.append(pkgNames);
        }
        sql.append(" and o.b in ");
        sql.append(filePath);
        try {
            initDatabase();
            int language = LanguageCompat.getCurrentLocaleID(CleanApplication.getInstance());
            cursor = db.rawQuery(sql.toString(), new String[]{String.valueOf(language)});
            List<CacheTrashBean> datas = CacheTrashBean.fromCursor(cursor);
            if (language != DEFAULT_LANGUAGE) {
                datas = filterCacheDatas(language, datas);
            }
            if (datas != null && !datas.isEmpty()) {// check datas
                final int length = datas.size();
                for (int i = length - 1; i >= 0; i--) {
                    if (TextUtils.isEmpty(datas.get(i).filePath)) {
                        datas.remove(i);
                    }
                }
            }
            return datas;
        } catch (Exception e) {
            if (FeatureConfig.DEBUG_LOG) {
                throw new RuntimeException(e);
            }
            return null;
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    private List<RegularTrashBean> filterRegularDatas(int language, List<RegularTrashBean> datas) {
        if (datas == null) {
            return null;
        }
        List<RegularTrashBean> result = new ArrayList<RegularTrashBean>();
        //1.分组
        List<RegularTrashBean> curLan = new ArrayList<RegularTrashBean>();
        List<RegularTrashBean> enLan = new ArrayList<RegularTrashBean>();
        for (RegularTrashBean cache : datas) {
            if (cache.lid == language) {
                if (!TextUtils.isEmpty(cache.itemName)) {
                    curLan.add(cache);
                }
            } else {
                enLan.add(cache);
            }
        }
        //2.判断赋值给result
        if (curLan.size() > 0) {
            if (curLan.size() >= enLan.size()) {
                result = curLan;
            } else if (curLan.size() < enLan.size()) {
                List<RegularTrashBean> temp = new ArrayList<RegularTrashBean>();
                for (RegularTrashBean curCache : curLan) {
                    for (RegularTrashBean enCache : enLan) {
                        if (curCache.id == enCache.id) {
                            temp.add(enCache);
                            break;
                        }
                    }
                }
                enLan.removeAll(temp);//从enLan里剔除curCache
                result.addAll(enLan);
                result.addAll(curLan);
            }
        } else {
            result = enLan;
        }
        return result;
    }

    /**
     * 过滤同一ID不同语种重复出现2次
     *
     * @param language
     * @param datas
     * @return
     */
    private List<CacheTrashBean> filterCacheDatas(int language, List<CacheTrashBean> datas) {
        if (datas == null) {
            return null;
        }
        List<CacheTrashBean> result = new ArrayList<CacheTrashBean>();
        //1.分组
        List<CacheTrashBean> curLan = new ArrayList<CacheTrashBean>();
        List<CacheTrashBean> enLan = new ArrayList<CacheTrashBean>();
        for (CacheTrashBean cache : datas) {
            if (cache.lid == language) {
                if (!TextUtils.isEmpty(cache.itemName)) {
                    curLan.add(cache);
                }
            } else {
                enLan.add(cache);
            }
        }
        //2.判断赋值给result
        if (curLan.size() > 0) {
            if (curLan.size() >= enLan.size()) {
                result = curLan;
            } else if (curLan.size() < enLan.size()) {
                List<CacheTrashBean> temp = new ArrayList<CacheTrashBean>();
                for (CacheTrashBean curCache : curLan) {
                    for (CacheTrashBean enCache : enLan) {
                        if (curCache.id == enCache.id) {
                            temp.add(enCache);
                            break;
                        }
                    }
                }
                enLan.removeAll(temp);//从enLan里剔除curCache
                result.addAll(enLan);
                result.addAll(curLan);
            }
        } else {
            result = enLan;
        }
        return result;
    }

    /**
     * get All whiteList
     *
     * @return
     */
    private List<WhiteListBean> getAllWhiteFilePath() {
        Cursor cursor = null;
        try {
            initDatabase();
            cursor = db.query(TABLENAMES[TABLE_WHITE_LIST], null, null, null, null, null, null);
            return WhiteListBean.fromCursor(cursor);
        } catch (Exception e) {
            if (FeatureConfig.DEBUG_LOG) {
                throw new RuntimeException(e);
            }
            return null;
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    /**
     * get the white paths list
     *
     * @return the paths if no white list return null
     */
    public String[] getWhiteList() {
        List<WhiteListBean> whiteFilePath = getAllWhiteFilePath();
        if (whiteFilePath == null) {
            return null;
        }
        int len = whiteFilePath.size();
        if (len < 1) {
            return null;
        } else {
            String[] paths = new String[whiteFilePath.size()];
            for (int i = 0; i < len; i++) {
                paths[i] = DataBaseDESUtil.decryptStr(whiteFilePath.get(i).filePath, KEY);
            }
            return paths;
        }
    }

    /**
     * clean  White List table and insert new records
     *
     * @param wbs can not contains null
     * @return is the operation is successful
     */
    public boolean cleanAndInsertIntoWhiteList(List<WhiteListBean> wbs) {
        if (wbs == null) {
            return false;
        }
        synchronized (mWriteLock) {
            try {
                initDatabase();
                db.beginTransaction();
                // clear
                db.delete(TABLENAMES[TABLE_WHITE_LIST], null, null);
                // insert
                ContentValues cv;
                for (WhiteListBean wb : wbs) {
                    cv = new ContentValues();
                    cv.put("b", wb.pkgName);
                    cv.put("c", wb.filePath);
                    cv.put("d", wb.type);
                    db.insert(TABLENAMES[TABLE_WHITE_LIST], null, cv);
                }
                db.setTransactionSuccessful();
                return true;
            } catch (Exception e) {
                if (FeatureConfig.DEBUG_LOG) {
                    throw new RuntimeException(e);
                }
                return false;
            } finally {
                if (db != null) {
                    if (db.inTransaction()) {
                        db.endTransaction();
                    }
                }
            }
        }
    }

    /**
     * get residual by pkg name
     *
     * @param pkgName encode pkgname
     * @return may return null
     */
    public List<ResidualTrashBean> getResidualByPkgName(String pkgName) {
        Cursor cursor = null;
        try {
            initDatabase();
            cursor = db.rawQuery(REDULE_SQL_BY_PKG, new String[]{
                    pkgName
            });
            List<ResidualTrashBean> residualTrashBeans = ResidualTrashBean.fromCursor(cursor, null);
            if (residualTrashBeans != null) {
                for (ResidualTrashBean rb : residualTrashBeans) {
                    rb.filePath = DataBaseDESUtil.decryptStr(rb.filePath, KEY);
                }
            }
            return residualTrashBeans;
        } catch (Exception e) {
            if (FeatureConfig.DEBUG_LOG) {
                throw new RuntimeException(e);
            }
            return null;
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    //根据不同类型，获取深度、快速、深度/快速数据
    public List<RegularTrashBean> getAllRegulars(List<String> pkgs, int queryType) {
        Cursor cursor = null;
        String pkgNamesSQL = getPkgNamesSQL(pkgs);
        if (TextUtils.isEmpty(pkgNamesSQL)) {
            return null;
        }
        String regularSql = "";
        if (queryType == DEEP) {
            regularSql = REGULARS_SQL_DEEP;
        } else if (queryType == QUICK) {
            regularSql = REGULARS_SQL_QUICK;
        } else if (queryType == ALL) {
            regularSql = REGULARS;
        }
        StringBuilder sql = new StringBuilder(regularSql);
        sql.append(pkgNamesSQL);
        try {
            initDatabase();
            int language = LanguageCompat.getCurrentLocaleID(CleanApplication.getInstance());
            cursor = db.rawQuery(sql.toString(), new String[]{String.valueOf(language)});
            List<RegularTrashBean> datas = RegularTrashBean.fromCursor(cursor);
            if (language != DEFAULT_LANGUAGE) {
                datas = filterRegularDatas(language, datas);
            }
            if (datas != null && !datas.isEmpty()) {// check datas
                final int length = datas.size();
                for (int i = length - 1; i >= 0; i--) {
                    if (TextUtils.isEmpty(datas.get(i).rootPath)) {
                        datas.remove(i);
                    }
                }
            }
            return datas;
        } catch (Exception e) {
            if (FeatureConfig.DEBUG_LOG) {
                throw new RuntimeException(e);
            }
            return null;
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    /**
     * 先将缓存/残留表中数据根据路径ID删除
     * 更新缓存/残留数据
     * 将删除类型入白名单，同时删除本地路径数据
     * sourceType 路径来源０本地1 云端
     */
    private void updateTable(List<PathUpdateInfo> infos, int sourceType, List<String> unKnownList) {
        if (infos == null || infos.isEmpty()) {
            return;
        }
        // 删除缓存/残留数据
        List<String> args = new ArrayList<String>();
        // 更新
        List<PathUpdateInfo> updates = new ArrayList<PathUpdateInfo>();
        //缓存数据
        List<CacheTrashBean> cacheBeans = new ArrayList<CacheTrashBean>();
        //残留数据
        List<ResidualTrashBean> residualBeans = new ArrayList<ResidualTrashBean>();

        //对删除数据/更新数据拆分
        for (int i = 0; i < infos.size(); i++) {
            PathUpdateInfo updateInfo = infos.get(i);
            String pathId = updateInfo.pathId;
            if (!TextUtils.isEmpty(pathId)) {
                args.add(pathId);// 保存所有需要删除路径ID

                if (updateInfo.type == UPDATE) {// 更新类型，需要插入到缓存/残留数据表中
                    updates.add(updateInfo);
                }
            }
        }

        //将更新数据拆分缓存/残留
        if (updates != null && !updates.isEmpty()) {
            if (FeatureConfig.DEBUG_LOG) {
                LogHelper.i(TraversalCacheAndResidualScanner.TAG_DB, "分拆缓存，残留数据开始");
            }
            spliteTrash(updates, sourceType, cacheBeans, residualBeans);
        }

        //同步db删除／更新操作
        synchronized (mWriteLock) {
            //删除数据
            if (!args.isEmpty()) {
                deleteData(args);
            }
            // 更新垃圾数据
            addTrashInfo(cacheBeans, residualBeans);
            // 更新未知路径
            if (unKnownList != null) {
                updateUnknownTable(unKnownList);
            }
        }
    }

    private void deleteData(List<String> args) {
        if (FeatureConfig.DEBUG_LOG) {
            LogHelper.i(TraversalCacheAndResidualScanner.TAG_DB, "删除缓存，残留数据");
        }
        // 删除缓存数据
        String cachesql = "DELETE FROM " + TABLENAMES[12] + " WHERE c in "
                + getPkgNamesSQL(args);
        try {
            initDatabase();
            db.execSQL(cachesql);
            // 删除残留数据
            String residualTrashsql = "DELETE FROM " + TABLENAMES[9] + " WHERE e in "
                    + getPkgNamesSQL(args);
            db.execSQL(residualTrashsql);

            // 删除路径表中数据
            if (FeatureConfig.DEBUG_LOG) {
                LogHelper.i(TraversalCacheAndResidualScanner.TAG_DB, "删除路径表数据");
            }
            String fileSql = "DELETE FROM " + TABLENAMES[5] + " WHERE a in "
                    + getPkgNamesSQL(args);
            db.execSQL(fileSql);
        } catch (Exception e) {
            if (FeatureConfig.DEBUG_LOG) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 添加垃圾信息
     */
    private void addTrashInfo(List<CacheTrashBean> cacheBeans, List<ResidualTrashBean> residualBeans) {
        long startTime = System.currentTimeMillis();

        if (!cacheBeans.isEmpty()) {
            if (FeatureConfig.DEBUG_LOG) {
                LogHelper.i(TraversalCacheAndResidualScanner.TAG_DB, "缓存数据更新开始");
            }
            updateCacheTable(cacheBeans);
            if (FeatureConfig.DEBUG_LOG) {
                LogHelper.i(TraversalCacheAndResidualScanner.TAG_DB, "缓存数据更新结束＝"
                        + (System.currentTimeMillis() - startTime));
            }
        }
        if (!residualBeans.isEmpty()) {
            if (FeatureConfig.DEBUG_LOG) {
                LogHelper.i(TraversalCacheAndResidualScanner.TAG_DB, "残留数据更新开始");
            }
            updateResidualTable(residualBeans);
            if (FeatureConfig.DEBUG_LOG) {
                LogHelper.i(TraversalCacheAndResidualScanner.TAG_DB, "残留数据更新结束＝"
                        + (System.currentTimeMillis() - startTime));
            }
        }
    }

    private void spliteTrash(List<PathUpdateInfo> updates, int sourceType,
                             List<CacheTrashBean> cacheBeans, List<ResidualTrashBean> residualBeans) {
        for (PathUpdateInfo info : updates) {
            List<PathUpdateInfo.DescriptionPackage> pkgs = info.pkgs;
            if (pkgs == null || pkgs.isEmpty()) {
                continue;
            }
            for (PathUpdateInfo.DescriptionPackage pkg : pkgs) {
                String packageName = Decode.encode(pkg.mPkg);
                if (info.inst_sugg != -1) {// cache
                    CacheTrashBean cacheBean = new CacheTrashBean();
                    cacheBean.fileId = Long.valueOf(info.pathId);
                    cacheBean.alertInfo = info.inst_desc;
                    cacheBean.pkgName = packageName;
                    cacheBean.deleteLevel = info.inst_sugg;
                    cacheBean.itemName = info.desc;
                    cacheBean.localName = info.localDesc;
                    cacheBean.updateTime = info.updateTime;
                    cacheBean.sourceType = sourceType;

                    cacheBean.filePath = FileUtils.removeSlash(info.path);
                    cacheBean.lid = info.lid;
                    cacheBean.localLid = info.localLid;
                    cacheBean.itemNameID = info.descID;
                    if (FeatureConfig.DEBUG_LOG) {
                        LogHelper.i(TraversalCacheAndResidualScanner.TAG_DB, cacheBean.toString());
                    }
                    cacheBeans.add(cacheBean);
                }
                if (info.unin_sugg != -1) {// residual
                    ResidualTrashBean residualBean = new ResidualTrashBean();
                    residualBean.fileId = Long.valueOf(info.pathId);
                    residualBean.alertInfo = info.unin_desc;
                    residualBean.pkgNames = packageName;
                    residualBean.deleteLevel = info.unin_sugg;
                    residualBean.softName = pkg.mAppName;
                    residualBean.updateTime = info.updateTime;
                    residualBean.sourceType = sourceType;
                    residualBean.filePath = FileUtils.removeSlash(info.path);
                    residualBean.lid = 1;
                    if (FeatureConfig.DEBUG_LOG) {
                        LogHelper.i(TraversalCacheAndResidualScanner.TAG_DB, residualBean.toString());
                    }
                    residualBeans.add(residualBean);
                }
            }
        }
    }

    // 添加／更新缓存表数据
    private void updateCacheTable(List<CacheTrashBean> cacheTrashBeans) {
        Cursor cursor = null;
        try {
            initDatabase();
            db.beginTransaction();
            for (CacheTrashBean cacheTrashBean : cacheTrashBeans) {
                long pid = -1;// 包名ID
                long fid = -1;// 路径ID
                long aid = -1;// 提示ID
                long did = -1;// 说明ID
                // 包名表，h表
                {
                    String sqlH = "select h.a from " + TABLENAMES[7] + " where h.b=?";
                    String selectionArgs[] = {
                            cacheTrashBean.pkgName
                    };
                    cursor = db.rawQuery(sqlH, selectionArgs);
                    if (!cursor.moveToFirst()) {
                        ContentValues values = new ContentValues();
                        values.put("b", cacheTrashBean.pkgName);
                        pid = db.insertWithOnConflict(TABLENAMES[7], null, values,
                                SQLiteDatabase.CONFLICT_IGNORE);
                    } else {
                        pid = cursor.getInt(cursor.getColumnIndex("a"));
                    }
                    close(cursor);
                }

                // 路径信息,f表
                {
                    String[] p = cacheTrashBean.filePath.split("/");
                    String rootPath = "";
                    if (p != null && p.length > 0) {
                        if (p[0].length() == 0) {
                            rootPath = Decode.encode(p[1]);
                        } else {
                            rootPath = Decode.encode(p[0]);
                        }
                    }
                    String sqlF = "select f.a, f.b from " + TABLENAMES[5] + " where f.b=?";
                    cacheTrashBean.filePath = DataBaseDESUtil.encryptStr(cacheTrashBean.filePath, KEY);
                    String selectionArgs[] = {
                            cacheTrashBean.filePath
                    };
                    Cursor cursorF = db.rawQuery(sqlF, selectionArgs);
                    if (!cursorF.moveToFirst()) {
                        String rootPathSql = "select o.a from " + TABLENAMES[14] + " where o.b=?";
                        Cursor cursorRoot = db.rawQuery(rootPathSql, new String[]{rootPath});
                        ContentValues values = new ContentValues();
                        long rootPathId = 0;
                        if (!cursorRoot.moveToFirst()) {
                            values.put("b", rootPath);
                            rootPathId = db.insertWithOnConflict(TABLENAMES[14], null, values,
                                    SQLiteDatabase.CONFLICT_IGNORE);
                        } else {
                            rootPathId = cursorRoot.getLong(cursorRoot.getColumnIndex("a"));
                        }
                        values.clear();
                        values.put("a", cacheTrashBean.fileId);
                        values.put("b", cacheTrashBean.filePath);
                        values.put("c", rootPathId);
                        values.put("d", cacheTrashBean.sourceType);
                        values.put("f", cacheTrashBean.updateTime);
                        db.insert("f", null, values);
                        fid = cacheTrashBean.fileId;
                        close(cursorRoot);
                    } else {
                        fid = cursorF.getLong(cursorF.getColumnIndex("a"));
                    }
                    close(cursorF);
                }
                // 提示说明，a表
                {
                    String sqlA = "select a.[a] from " + TABLENAMES[0]
                            + " where a.b=? and a.c=?";
                    String selectionArgs[] = {
                            cacheTrashBean.alertInfo, cacheTrashBean.lid + ""
                    };
                    cursor = db.rawQuery(sqlA, selectionArgs);
                    if (!cursor.moveToFirst()) {
                        ContentValues values = new ContentValues();
                        values.put("b", cacheTrashBean.alertInfo);
                        values.put("c", cacheTrashBean.lid);
                        aid = db.insertWithOnConflict(TABLENAMES[0], null, values,
                                SQLiteDatabase.CONFLICT_IGNORE);
                    } else {
                        aid = cursor.getLong(cursor.getColumnIndex("a"));
                    }
                    close(cursor);
                }
                // itemname,e表
                {
                    //插入en数据
                    String sqlE = "select e.a from " + TABLENAMES[4] + " where e.b=? and e.c=?";
                    String selectionArgs[] = {
                            cacheTrashBean.lid + "", cacheTrashBean.itemName
                    };
                    cursor = db.rawQuery(sqlE, selectionArgs);
                    if (!cursor.moveToFirst()) {
                        ContentValues values = new ContentValues();
                        values.put("a", cacheTrashBean.itemNameID);
                        values.put("b", cacheTrashBean.lid);
                        values.put("c", cacheTrashBean.itemName);
                        did = db.insertWithOnConflict(TABLENAMES[4], null, values,
                                SQLiteDatabase.CONFLICT_IGNORE);
                    } else {
                        did = cursor.getLong(cursor.getColumnIndex("a"));
                    }
                    close(cursor);
                    //插入其他语言数据
                    if (!TextUtils.isEmpty(cacheTrashBean.localName) && cacheTrashBean.localLid != 1) {
                        String sqlE2 = "select e.a from " + TABLENAMES[4] + " where e.b=? and e.c=?";
                        String selectionArgs2[] = {
                                cacheTrashBean.localLid + "", cacheTrashBean.localName
                        };
                        cursor = db.rawQuery(sqlE2, selectionArgs2);
                        if (!cursor.moveToFirst()) {
                            ContentValues values = new ContentValues();
                            values.put("a", cacheTrashBean.itemNameID);
                            values.put("b", cacheTrashBean.localLid);
                            values.put("c", cacheTrashBean.localName);
                            db.insertWithOnConflict(TABLENAMES[4], null, values,
                                    SQLiteDatabase.CONFLICT_IGNORE);
                        } else {
                            cursor.getLong(cursor.getColumnIndex("a"));
                        }
                        close(cursor);
                    }
                }
                // 主表数据，m
                {
                    String sqlM = "select m.a from " + TABLENAMES[12] + " where m.b=? and m.c=? ";
                    String selectionArgs[] = {
                            pid + "", fid + ""
                    };
                    cursor = db.rawQuery(sqlM, selectionArgs);
                    ContentValues values = new ContentValues();
                    values.put("b", pid);
                    values.put("c", fid);
                    values.put("e", aid);
                    values.put("g", cacheTrashBean.deleteLevel);
                    values.put("h", cacheTrashBean.contentType);
                    values.put("i", did);
                    if (!cursor.moveToFirst()) {
                        long id = db.insert(TABLENAMES[12], null, values);
                        if (FeatureConfig.DEBUG_LOG) {
                            LogHelper.i(TraversalCacheAndResidualScanner.TAG_DB,
                                    "缓存数据插入=路径id " + fid + " 路径＝" + DataBaseDESUtil.decryptStr(cacheTrashBean.filePath, KEY)
                                            + " 包名＝" + cacheTrashBean.pkgName);
                        }
                    } else {
                        long id = db.update(TABLENAMES[12], values, "m.b=? and m.c=?",
                                selectionArgs);
                        if (FeatureConfig.DEBUG_LOG) {
                            LogHelper.i(TraversalCacheAndResidualScanner.TAG_DB,
                                    "缓存数据更新=路径id " + fid + " 路径＝" + DataBaseDESUtil.decryptStr(cacheTrashBean.filePath, KEY)
                                            + " 包名＝" + cacheTrashBean.pkgName);
                        }
                    }
                    close(cursor);
                }
            }
            db.setTransactionSuccessful();
        } catch (Exception e) {
            if (FeatureConfig.DEBUG_LOG) {
                throw new RuntimeException(e);
            }
        } finally {
            close(cursor);
            if (null != db) {
                db.endTransaction();
            }
        }
    }

    // 增量更新卸载残留表数据
    private void updateResidualTable(List<ResidualTrashBean> residualTrashBeans) {
        try {
            initDatabase();
            db.beginTransaction();
            for (ResidualTrashBean residualTrashBean : residualTrashBeans) {
                long aid = -1; // a表主键
                long fid = -1;
                // 提示说明，a表
                {
                    String sqlA = "select a.[a] from " + TABLENAMES[0] + " where a.b=? and a.c=?";
                    String selectionArgs[] = {
                            residualTrashBean.alertInfo, residualTrashBean.lid + ""
                    };
                    Cursor cursor = db.rawQuery(sqlA, selectionArgs);
                    if (!cursor.moveToFirst()) {
                        ContentValues values = new ContentValues();
                        values.put("b", residualTrashBean.alertInfo);
                        values.put("c", residualTrashBean.lid);
                        aid = db.insertWithOnConflict(TABLENAMES[0], null, values, SQLiteDatabase.CONFLICT_IGNORE);
                    } else {
                        aid = cursor.getLong(cursor.getColumnIndex("a"));
                    }
                    close(cursor);
                }
                // 路径信息,f表
                {
                    String[] p = residualTrashBean.filePath.split("/");
                    String rootPath = "";
                    if (p != null && p.length > 0) {
                        if (p[0].length() == 0) {
                            rootPath = Decode.encode(p[1]);
                        } else {
                            rootPath = Decode.encode(p[0]);
                        }
                    }
                    residualTrashBean.filePath = DataBaseDESUtil
                            .encryptStr(residualTrashBean.filePath, KEY);
                    String sqlF = "select f.b, f.a from " + TABLENAMES[5]
                            + " where f.b=?";
                    String selectionArgs[] = {
                            residualTrashBean.filePath
                    };
                    Cursor cursorF = db.rawQuery(sqlF, selectionArgs);
                    if (!cursorF.moveToFirst()) {
                        String rootPathSql = "select o.a from " + TABLENAMES[14] + " where o.b=?";
                        Cursor cursorRoot = db.rawQuery(rootPathSql, new String[]{rootPath});
                        ContentValues values = new ContentValues();
                        long rootPathId = 0;
                        if (!cursorRoot.moveToFirst()) {
                            values.put("b", rootPath);
                            rootPathId = db.insertWithOnConflict(TABLENAMES[14], null, values,
                                    SQLiteDatabase.CONFLICT_IGNORE);
                        } else {
                            rootPathId = cursorRoot.getLong(cursorRoot.getColumnIndex("a"));
                        }

                        values.clear();
                        values.put("a", residualTrashBean.fileId);
                        values.put("b", residualTrashBean.filePath);
                        values.put("c", rootPathId);
                        values.put("d", residualTrashBean.sourceType);
                        values.put("f", residualTrashBean.updateTime);
                        db.insert("f", null, values);
                        fid = residualTrashBean.fileId;
                        close(cursorRoot);
                    } else {
                        fid = cursorF.getLong(cursorF.getColumnIndex("a"));
                    }
                    close(cursorF);
                }
                // 主表信息添加,j表信息
                {
                    String sqlJ = "select j.c from " + TABLENAMES[9] + " where j.e=?";
                    String selectionArgs[] = {
                            fid + ""
                    };
                    Cursor cursor = db.rawQuery(sqlJ, selectionArgs);
                    if (!cursor.moveToFirst()) { // 查询不到数据，直接插入一条数据
                        ContentValues values = new ContentValues();
                        values.put("c", residualTrashBean.pkgNames);
                        values.put("d", residualTrashBean.softName);
                        values.put("e", fid);
                        values.put("f", aid);
                        values.put("h", residualTrashBean.deleteLevel);
                        long id = db.insert(TABLENAMES[9], null, values);
                        if (FeatureConfig.DEBUG_LOG) {
                            LogHelper.i(TraversalCacheAndResidualScanner.TAG_DB,
                                    "残留数据插入＝路径id " + fid + " 路径＝" + DataBaseDESUtil.decryptStr(residualTrashBean.filePath, KEY)
                                            + " 包名＝" + residualTrashBean.pkgNames);
                        }
                    } else {
                        String pkg = cursor.getString(cursor.getColumnIndex("c"));
                        if (TextUtils.isEmpty(pkg)) {
                            continue;
                        }
                        String pkgs[] = pkg.split("[+]");
                        if (!Arrays.asList(pkgs).contains(residualTrashBean.pkgNames)) {// 查询到数据，如果不包含此包名，则加入后更新
                            String whereClause = "j.e=?";
                            ContentValues values = new ContentValues();
                            values.put("c", pkg + "+" + residualTrashBean.pkgNames);
                            long id = db.update(TABLENAMES[9], values, whereClause, selectionArgs);
                            if (FeatureConfig.DEBUG_LOG) {
                                LogHelper.i(TraversalCacheAndResidualScanner.TAG_DB,
                                        "残留数据更新＝路径id " + fid + " 路径＝" + DataBaseDESUtil
                                                .decryptStr(residualTrashBean.filePath, KEY) + " 包名＝"
                                                + residualTrashBean.pkgNames);
                            }
                        }
                    }
                    close(cursor);
                }
            }
            db.setTransactionSuccessful();
        } catch (Exception e) {
            if (FeatureConfig.DEBUG_LOG) {
                throw new RuntimeException(e);
            }
        } finally {
            if (null != db) {
                db.endTransaction();
            }
        }
    }

    public void close(Cursor cursor) {
        if (cursor != null && !cursor.isClosed()) {
            cursor.close();
        }
    }

    //    public void closeDB() {
    //        if (db != null && db.isOpen()) {
    //            db.close();
    //        }
    //    }

    /**
     * 更新未知垃圾路径到n表，在下次递归路径可跳过
     */
    private void updateUnknownTable(List<String> beans) {
        Cursor cursor = null;
        try {
            initDatabase();
            db.beginTransaction();
            ContentValues cv = new ContentValues();
            for (String path : beans) {
                cursor = db.query(TABLENAMES[13], null, "b=?", new String[]{
                        path
                }, null, null, null);
                if (cursor == null || cursor.getCount() == 0) {
                    cv.clear();
                    cv.put("b", path);
                    db.insert(TABLENAMES[13], null, cv);
                    if (FeatureConfig.DEBUG_LOG) {
                        LogHelper.i(TraversalCacheAndResidualScanner.TAG_DB, "加入未知路径＝" + path);
                    }
                }
                close(cursor);
            }
            db.setTransactionSuccessful();
        } catch (Exception e) {
            if (FeatureConfig.DEBUG_LOG) {
                throw new RuntimeException(e);
            }
        } finally {
            close(cursor);
            if (null != db) {
                db.endTransaction();
            }
        }
    }

    /**
     * @return 获取未知路径
     */
    public List<FilePathBean> getUnknownPath() {
        Cursor cursor = null;
        try {
            initDatabase();
            cursor = db.query(TABLENAMES[13], new String[]{
                    "b"
            }, null, null, null, null, null);
            if (cursor != null) {
                List<FilePathBean> beans = new ArrayList<FilePathBean>();
                int index = cursor.getColumnIndex("b");
                while (cursor.moveToNext()) {
                    FilePathBean filePathBean = new FilePathBean();
                    filePathBean.path = cursor.getString(index);
                    if (TextUtils.isEmpty(filePathBean.path)) {
                        continue;
                    }
                    beans.add(filePathBean);
                }
                return beans;
            }
        } catch (Exception e) {
            if (FeatureConfig.DEBUG_LOG) {
                throw new RuntimeException(e);
            }
            return null;
        } finally {
            close(cursor);
        }
        return null;
    }

    /**
     * 返回白名单路径
     */
    public List<FilePathBean> getWhitePath() {
        String whitePath[] = getWhiteList();
        List<FilePathBean> beans = null;
        if (whitePath != null) {
            beans = new ArrayList<FilePathBean>();
            for (String path : whitePath) {
                FilePathBean bean = new FilePathBean();
                bean.path = path;
                beans.add(bean);
            }
        }
        return beans;
    }

    /**
     * 设置本地未知路径失效
     */
    public void setUnknownPathInvalid() {
        String dropSql = "DROP TABLE IF EXISTS n";
        String createSql = "create table n  (a INTEGER   PRIMARY KEY AUTOINCREMENT,b text null )";
        try {
            initDatabase();
            db.execSQL(dropSql);
            db.execSQL(createSql);
        } catch (Exception e) {
            if (FeatureConfig.DEBUG_LOG) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * @return 获得云端下发到本地库路径数据
     * 每次获取固定数量路径请求，采用轮询的方式
     */
    public List<FilePathBean> getCloudPath() {
        Cursor cursor = null;
        try {
            initDatabase();
            cursor = db.query(TABLENAMES[5], new String[]{
                    "a", "f"
            }, "d=? and a>?", new String[]{
                    "1", String.valueOf(TrashCloudSharedPrefManager.getCloudLastCloudId())
            }, null, null, "a ASC", ClOUD_UPDATE_NUM);
            if (cursor != null) {
                List<FilePathBean> beans = new ArrayList<FilePathBean>();
                while (cursor.moveToNext()) {
                    FilePathBean bean = new FilePathBean();
                    bean.id = cursor.getLong(cursor.getColumnIndex("a"));
                    bean.updateTime = cursor.getLong(cursor.getColumnIndex("f"));
                    beans.add(bean);
                }
                if (beans.size() > 0) {
                    if (beans.size() < 10) {
                        TrashCloudSharedPrefManager.setCloudLastUpdateId(0);
                    } else {
                        TrashCloudSharedPrefManager.setCloudLastUpdateId(beans.get(beans.size() - 1).id);
                    }
                    return beans;
                }
            }
        } catch (Exception e) {
            if (FeatureConfig.DEBUG_LOG) {
                LogHelper.i(TraversalCacheAndResidualScanner.TAG_DB, "云端数据获取异常" + e);
                throw new RuntimeException(e);
            }
            return null;
        } finally {
            close(cursor);
        }
        return null;
    }

    /**
     * @return 获取数据库版本
     */
    public int getDBVersion() {
        try {
            initDatabase();
            return db.getVersion();
        } catch (Exception e) {
            if (FeatureConfig.DEBUG_LOG) {
                throw new RuntimeException(e);
            }
            return DATABASE_VERSION;
        }
    }

    /**
     * @return 本地更新最大时间
     */
    public long nativeUpdateMaxTime() {
        long maxTime = 0;
        Cursor cursor = null;
        try {
            String sql = "SELECT max(f.f) f FROM f WHERE f.d=0";
            initDatabase();
            cursor = db.rawQuery(sql, null);
            if (cursor != null) {
                if (cursor.moveToFirst()) {
                    maxTime = cursor.getLong(cursor.getColumnIndex("f"));
                }
                cursor.close();
            }
        } catch (Exception e) {
            if (FeatureConfig.DEBUG_LOG) {
                throw new RuntimeException(e);
            }
            return NATIVE_TIME;
        } finally {
            close(cursor);
        }
        return maxTime > NATIVE_TIME ? maxTime : NATIVE_TIME;
    }

    public void updateDataHandler(List<String> unKownPaths, List<PathUpdateInfo> updateInfos,
                                  int updateType) {
        switch (updateType) {
            case UNKNOWN_UPDATE:
                unKnowPathSplit(unKownPaths, updateInfos);
                break;
            case NATIVE_UPDATE:
                updateTable(updateInfos, TrashDB.UPDATE, null);
                break;
            case CLOUD_UPDATE:
                updateTable(updateInfos, TrashDB.CLOUD, null);
                break;

            default:
                break;
        }
    }

    /**
     * @param unKownPaths
     * @param updateInfos 将云端数据与未知数据比对 拆分出未知路径,云端路径
     */
    private void unKnowPathSplit(List<String> unKownPaths, List<PathUpdateInfo> updateInfos) {
        if (unKownPaths == null) {
            return;
        }
        List<String> paths = new ArrayList<String>(unKownPaths);
        long startTime = System.currentTimeMillis();
        if (updateInfos != null && !updateInfos.isEmpty()) {
            for (PathUpdateInfo info : updateInfos) {
                if (TextUtils.isEmpty(info.path)) {
                    continue;
                }
                for (String s : unKownPaths) {
                    if (info.path.startsWith(s)) {
                        paths.remove(s);
                    }
                }
            }
            //更新本地垃圾数据
            if (FeatureConfig.DEBUG_LOG) {
                LogHelper.i(TraversalCacheAndResidualScanner.TAG_DB, "更新本地垃圾数据(云端)");
            }
            updateTable(updateInfos, CLOUD, paths);
            if (FeatureConfig.DEBUG_LOG) {
                LogHelper.i(TraversalCacheAndResidualScanner.TAG_DB,
                        "更新本地垃圾数据(云端)＝" + (System.currentTimeMillis() - startTime));
            }
        }

    }

    /**
     * 获取高频请求服务器的路径名单
     *
     * @return
     */
    public static String[] getServerRequestWhiteList() {
        return WHITELIST;
    }

    /**
     * 获取应用退出到home界面，扫描到的缓存数据
     *
     * @return
     */
    public List<CacheTrashBean> findCacheApp(String pkgName) {
        Cursor cursor = null;
        final StringBuilder sql = new StringBuilder();
        sql.append(CACHE_SQL_QUICK);
        sql.append(" and h.b=" + pkgName);

        try {
            initDatabase();
            int language = LanguageCompat.getCurrentLocaleID(CleanApplication.getInstance());
            cursor = db.rawQuery(sql.toString(), new String[]{String.valueOf(language)});
            List<CacheTrashBean> datas = CacheTrashBean.fromCursor(cursor);
            if (language != DEFAULT_LANGUAGE) {
                datas = filterCacheDatas(language, datas);
            }
            if (datas != null && !datas.isEmpty()) {// check datas
                final int length = datas.size();
                for (int i = length - 1; i >= 0; i--) {
                    if (TextUtils.isEmpty(datas.get(i).filePath)) {
                        datas.remove(i);
                    }
                }
            }
            return datas;
        } catch (Exception e) {
            if (FeatureConfig.DEBUG_LOG) {
                throw new RuntimeException(e);
            }
            return null;
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    /**
     * 获取专清应用缓存
     */
    public List<CacheTrashBean> findAppCleanCache(List<String> pkgNames) {
        Cursor cursor = null;
        String pkgNamesSQL = getPkgNamesSQL(pkgNames);
        if (TextUtils.isEmpty(pkgNamesSQL)) {
            return null;
        }
        final StringBuilder sql = new StringBuilder();
        sql.append(CACHE);
        sql.append(" and h.b in ");
        sql.append(pkgNamesSQL);
        if (FeatureConfig.DEBUG_LOG) {
            LogHelper.d(TAG, "sql = " + sql.toString());
        }
        try {
            initDatabase();
            int language = LanguageCompat.getCurrentLocaleID(CleanApplication.getInstance());
            cursor = db.rawQuery(sql.toString(), new String[]{String.valueOf(language)});
            List<CacheTrashBean> datas = CacheTrashBean.fromCursor(cursor);
            if (language != DEFAULT_LANGUAGE) {
                datas = filterCacheDatas(language, datas);
            }
            if (datas != null && !datas.isEmpty()) {// check datas
                final int length = datas.size();
                for (int i = length - 1; i >= 0; i--) {
                    if (TextUtils.isEmpty(datas.get(i).filePath)) {
                        datas.remove(i);
                    }
                }
            }
            return datas;
        } catch (Exception e) {
            if (FeatureConfig.DEBUG_LOG) {
                throw new RuntimeException(e);
            }
            return null;
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    /**
     * 获取 应用缓存信息的入口
     *
     * @param sql
     * @return
     */
    List<CacheTrashBean> getCacheTrash(String sql) {
        if (TextUtils.isEmpty(sql)) {
            if (FeatureConfig.DEBUG_LOG) {
                throw new IllegalArgumentException("sql is null or zero length");
            }
            return Collections.emptyList();
        }
        Cursor cursor = null;
        try {
            initDatabase();
            int language = LanguageCompat.getCurrentLocaleID(CleanApplication.getInstance());
            cursor = db.rawQuery(sql, new String[]{String.valueOf(language)});
            List<CacheTrashBean> datas = CacheTrashBean.fromCursor(cursor);
            if (language != DEFAULT_LANGUAGE) {
                datas = filterCacheDatas(language, datas);
            }
            if (datas != null && !datas.isEmpty()) {// check datas
                final int length = datas.size();
                for (int i = length - 1; i >= 0; i--) {
                    CacheTrashBean cacheTrashBean = datas.get(i);
                    if (TextUtils.isEmpty(cacheTrashBean.filePath)) {
                        datas.remove(i);
                    } else {
                        cacheTrashBean.filePath = DataBaseDESUtil.decryptStr(cacheTrashBean.filePath, KEY);
                    }
                }
            }
            return datas;
        } catch (Exception e) {
            if (FeatureConfig.DEBUG_LOG) {
                throw new RuntimeException(e);
            }
            return Collections.emptyList();
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    /**
     * 获取残留信息入口
     *
     * @param sql
     * @param df  已安装程序过滤器
     * @return
     */
    List<ResidualTrashBean> getResidualTrash(String sql, DBBeanFilter<ResidualTrashBean> df) {
        if (TextUtils.isEmpty(sql)) {
            if (FeatureConfig.DEBUG_LOG) {
                throw new IllegalArgumentException("sql is null or zero length");
            }
            return Collections.emptyList();
        }
        Cursor cursor = null;
        try {
            initDatabase();
            cursor = db.rawQuery(sql, null);
            List<ResidualTrashBean> datas;
            if (df == null) {
                datas = ResidualTrashBean.fromCursor(cursor, null);
            } else {
                datas = ResidualTrashBean.fromCursor(cursor, df);
            }
            if (datas != null && !datas.isEmpty()) {// check datas
                final int length = datas.size();
                for (int i = length - 1; i >= 0; i--) {
                    ResidualTrashBean residualTrashBean = datas.get(i);
                    if (TextUtils.isEmpty(residualTrashBean.filePath)) {
                        datas.remove(i);
                    } else {
                        residualTrashBean.filePath = DataBaseDESUtil.decryptStr(residualTrashBean.filePath, KEY);
                    }
                }
            }
            return datas;
        } catch (Exception e) {
            if (FeatureConfig.DEBUG_LOG) {
                throw new RuntimeException(e); // 推荐在 DEBUG 模式下，重新抛出这个异常，以便尽早的发现问题
            }
            return Collections.emptyList();
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    /**
     * 获取应用缓存正则信息入口
     *
     * @param sql
     * @return
     */
    public List<RegularTrashBean> getRegularTrash(String sql) {
        if (TextUtils.isEmpty(sql)) {
            if (FeatureConfig.DEBUG_LOG) {
                throw new IllegalArgumentException("sql is null or zero length");
            }
            return Collections.emptyList();
        }
        Cursor cursor = null;
        try {
            initDatabase();
            int language = LanguageCompat.getCurrentLocaleID(CleanApplication.getInstance());
            cursor = db.rawQuery(sql, new String[]{String.valueOf(language)});
            List<RegularTrashBean> datas = RegularTrashBean.fromCursor(cursor);
            if (language != DEFAULT_LANGUAGE) {
                datas = filterRegularDatas(language, datas);
            }
            if (datas != null && !datas.isEmpty()) {// check datas
                final int length = datas.size();
                for (int i = length - 1; i >= 0; i--) {
                    if (TextUtils.isEmpty(datas.get(i).rootPath)) {
                        datas.remove(i);
                    }
                }
            }
            return datas;
        } catch (Exception e) {
            if (FeatureConfig.DEBUG_LOG) {
                throw new RuntimeException(e);
            }
            return null;
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }
}

